import random
import tkinter as tk

data = {}  # 存储雷区数据的字典
obj_data = {}  # 存储按钮对象的字典
GAME_CONDITION = False  # 游戏状态，True 表示正常运行
time = -2  # 计时器

# 游戏参数
x = 20  # 地图宽度
y = 20  # 地图高度
bomber = 40  # 地雷数量
win_count = x * y - bomber  # 获胜条件：未揭示的格子数量
count = 0  # 已揭示的格子数量

# 计算某一格周围雷的数量
def Besideboom(data, eachx, eachy):
    num = 0
    for i in range(-1, 2):
        for j in range(-1, 2):
            if i != 0 or j != 0:
                if f"{eachx + i}.{eachy + j}" in data:
                    if data[f"{eachx + i}.{eachy + j}"] == 9:
                        num = num + 1
    return num

# 主菜单界面
def main_meun(window):
    clear_all(window)
    frm = tk.Frame(window, bg="#666666")
    lbl = tk.Label(frm, text='扫雷※', font=("Helvetica", 90), bg="#666666", width=10)

    btn_start = tk.Button(frm, text="开始", font=("Helvetica", 30), width=30, command=lambda: creat_game(window, x, y, bomber))

    btn_fix = tk.Button(frm, text="设置", font=("Helvetica", 30), width=30, command=lambda: change_val(window))

    btn_exit = tk.Button(frm, text="退出", font=("Helvetica", 30), width=30, command=lambda: close_window(window))

    lbl.pack(ipady=5, ipadx=5)
    btn_start.pack(pady=5, ipadx=5, ipady=5)
    btn_fix.pack(pady=5, ipadx=5, ipady=5)
    btn_exit.pack(pady=5, ipadx=5, ipady=5)
    frm.pack()

# 创建游戏界面
def creat_game(window, x=x, y=y, bomber=bomber, win_count=win_count):
    # 初始化数据
    clear_all(window)
    GAME_CONDITION = True
    global time, count
    time = -2
    count = 0

    # 侧栏
    frm_sider = tk.Frame(window, bg="#adadad")
    lbl = tk.Label(frm_sider, text='用时', bg='#adadad', font=("Helvetica", 20), anchor='n')
    timer = tk.Label(frm_sider, text="0", bg='#adadad', font=("Helvetica", 30), anchor='n')

    # 计时器
    def clock():
        global time
        time += 1
        timer.config(text=f"{time}")
        timer_id = window.after(1000, clock)

        if not GAME_CONDITION: 
            time = -1
            window.after_cancel(timer_id)

    btn_back = tk.Button(frm_sider, text='\n返回主菜单\n', font=("Helvetica", 8), command=lambda: main_meun(window), anchor="s")

    lbl.pack()
    timer.pack()
    btn_back.pack()

    # 游戏地图
    frm_main = tk.Frame(window)
    global obj_data, data
    mine = 0

    data = {}
    obj_data = {}
    for i in range(0, x):
        for j in range(0, y):
            data[f"{i}.{j}"] = 0
    while mine < bomber:
        if data[f"{random.randint(0, x-1)}.{random.randint(0, y-1)}"] == 9:
            continue
        else:
            mine = mine + 1
            data[f"{random.randint(0, x-1)}.{random.randint(0, y-1)}"] = 9
    for i in range(0, x):
        for j in range(0, y):
            if data[f"{i}.{j}"] != 9:
                data[f"{i}.{j}"] = Besideboom(data, i, j)

    for i in range(0, x):
        for j in range(0, y):
            obj_data[f"{i}.{j}"] = Buttons(frm_main, i, j)

    frm_sider.grid(row=0, column=0, ipady=10, ipadx=2, sticky='ns')
    frm_main.grid(row=0, column=1)
    clock()

# 设置界面
def change_val(window):
    clear_all(window)
 
    global x, y, bomber

    now_x = x
    now_y = y
    now_bomber = bomber   

    def change_x(value):
        scl_bomber.config(from_=int(int(value) * now_y / 20), to=int(int(value) * now_y / 5))
        scl_bomber.set(int(int(value) * now_y / 10))
        now_x = int(value)

    def change_y(value):
        scl_bomber.config(from_=int(int(value) * now_x / 20), to=int(int(value) * now_x / 5))
        scl_bomber.set(int(int(value) * now_x / 10))
        now_y = int(value)

    def sure_4_change():
        global x, y, bomber, win_count
        x = int(scl_x.get())
        y = int(scl_y.get())
        bomber = int(scl_bomber.get())
        win_count = x * y - bomber
        main_meun(window)

    def menu():
        main_meun(window)


    lbl_x = tk.Label(window,text='横向格数',height=3, font=('Helvetica', 20))
    scl_x = tk.Scale(window, from_=0, to=40, width=5, length=400, orient='horizontal', command=change_x)
    scl_x.set(x)

    lbl_y = tk.Label(window,text='横向格数',height=3, font=('Helvetica', 20))
    scl_y = tk.Scale(window, from_=0, to=40, width=5, length=400, orient='horizontal', command=change_y)
    scl_y.set(y)

    lbl_bomber = tk.Label(window,text='地雷数量',height=3, font=('Helvetica', 20))
    scl_bomber = tk.Scale(window, from_=int(x * y / 20), to=int(x * y / 5), width=5, length=400, orient='horizontal')
    scl_bomber.set(bomber)

    btn_4_sure = tk.Button(window, text='确认', height=3, font=('Helvetica', 20), width=20, command=sure_4_change, bg='#caabea')
    btn_menu = tk.Button(window, text='取消', height=3, font=('Helvetica', 20), width=20, command=menu, bg='#caabea')

    lbl_x.grid(column=0, row=0)
    scl_x.grid(column=1, row=0)
    lbl_y.grid(column=0, row=1)
    scl_y.grid(column=1, row=1)
    lbl_bomber.grid(column=0, row=2)
    scl_bomber.grid(column=1, row=2)
    btn_4_sure.grid(column=0, row=3, padx=5)
    btn_menu.grid(column=1, row=3, padx=5)
    

# 按钮类，用于表示游戏中的每个格子
class Buttons:
    def __init__(self, root, posx, posy):
        self.root = root
        self.button = tk.Button(root, bg="#74bce9", padx=12, pady=2, command=self.light)
        self.posx = posx
        self.posy = posy
        self.flag = True    # 是否揭示过

        self.button.grid(row=self.posx, column=self.posy)

    # 点击格子时触发的事件
    def light(self):
        global data, obj_data, GAME_CONDITION, count, win_count

        self.button.destroy()

        # 若为雷，游戏结束
        if data[f"{self.posx}.{self.posy}"] == 9:
            GAME_CONDITION = False
            for i in range(0, x):
                for j in range(0, y):
                    obj_data[f"{i}.{j}"].boom_light()
            window.after(4000, boom)
            return 

        self.flag = False            
        count += 1

        # 若周围无雷，自动揭示周围格子
        if data[f"{self.posx}.{self.posy}"] == 0:
            for i in range(-1,2):
                for j in range(-1, 2):
                    if f'{self.posx + i}.{self.posy + j}' in obj_data:
                        if obj_data[f"{self.posx + i}.{self.posy + j}"].flag:
                            obj_data[f"{self.posx + i}.{self.posy + j}"].light()

            value = " "

        else:
            value = str(data[f"{self.posx}.{self.posy}"])

        # 揭示格子
        self.label = tk.Label(master=self.root, text=value, padx=9, pady=5)
        self.label.grid(row=self.posx, column=self.posy)

        # 判断是否获胜
        if count == win_count:
            win()

    # 游戏失败时揭示地雷
    def boom_light(self):
        if self.flag:
            if data[f"{self.posx}.{self.posy}"] == 9:
                value = "※"
            elif data[f"{self.posx}.{self.posy}"] == 0:
                value = " "
            else:
                value = str(data[f"{self.posx}.{self.posy}"])

            self.button.destroy()

            self.label = tk.Label(master=self.root, text=value, padx=9, pady=5)
            if value == '※':
                self.label.config(fg='red', bg='#7ab0d8')
            self.label.grid(row=self.posx, column=self.posy)

# 胜利界面
def win():
    clear_all(window)
    lbl_w = tk.Label(window, text='You Win!',font=("Helvetica", 80), fg="#00fff4")
    lbl_t = tk.Label(window, text=f'用时：{time}',font=("Helvetica", 40))
    btn_win = tk.Button(window, text="新游戏",bg="#74bce9",command=lambda: creat_game(window), font=("Helvetica", 60))
    btn_main_menu = tk.Button(window, text="回到主菜单",bg="#74bce9",command=lambda: main_meun(window), font=("Helvetica", 60))

    lbl_w.pack(fill="both", pady=5, ipadx=5, ipady=5)
    lbl_t.pack(fill='both')
    btn_win.pack(fill="both", pady=5, ipadx=5, ipady=5)
    btn_main_menu.pack(fill="both", pady=5, ipadx=5, ipady=5)    

# 清除窗口上的所有组件
def clear_all(window):
    for widget in window.winfo_children():
        widget.destroy()

# 游戏失败界面
def boom():
    clear_all(window)
    lbl = tk.Label(window, text='Game Over',font=("Helvetica", 80), fg="red")
    btn_gameover = tk.Button(window, text="新游戏",bg="#74bce9",command=lambda: creat_game(window), font=("Helvetica", 60))
    btn_main_menu = tk.Button(window, text="回到主菜单",bg="#74bce9",command=lambda: main_meun(window), font=("Helvetica", 60))

    lbl.pack(fill="both", pady=5, ipadx=5, ipady=5)
    btn_gameover.pack(fill="both", pady=5, ipadx=5, ipady=5)
    btn_main_menu.pack(fill="both", pady=5, ipadx=5, ipady=5)

# 关闭窗口
def close_window(window):
    window.destroy()

# 主程序入口
if __name__ == "__main__":
    window = tk.Tk()
    main_meun(window)
    window.mainloop()
